package online.heycm.rbac.domain.user.repository;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import online.heycm.platform.common.entity.exception.Assert;
import online.heycm.platform.common.entity.result.Optional;
import online.heycm.platform.log.Logger;
import online.heycm.platform.log.LoggerManager;
import online.heycm.rbac.common.cache.RoleCache;
import online.heycm.rbac.common.constant.ErrorCode;
import online.heycm.rbac.common.utils.MPWrapper;
import online.heycm.rbac.common.utils.PageHelper;
import online.heycm.rbac.domain.user.aggregate.ConsoleUser;
import online.heycm.rbac.domain.user.repository.entity.UserAuthorize;
import online.heycm.rbac.domain.user.repository.entity.UserProfile;
import online.heycm.rbac.domain.user.repository.entity.UserRole;
import online.heycm.rbac.domain.user.repository.mapper.UserAuthorizeMapper;
import online.heycm.rbac.domain.user.repository.mapper.UserProfileMapper;
import online.heycm.rbac.domain.user.repository.mapper.UserRoleMapper;
import online.heycm.rbac.param.user.UserPageReq;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

/**
 * 用户仓储层
 *
 * @author hey
 * @version 1.0
 * @date 2024/1/12 17:24
 */
@Component
public class UserRepository {

    private static final Logger LOGGER = LoggerManager.getLogger(UserRepository.class);

    @Autowired
    private UserProfileMapper userProfileMapper;

    @Autowired
    private UserAuthorizeMapper userAuthorizeMapper;

    @Autowired
    private UserRoleMapper userRoleMapper;

    public Optional<UserAuthorize> queryAuthorize(int athType, String authIdentifier) {
        final LambdaQueryWrapper<UserAuthorize> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserAuthorize::getAuthType, athType);
        wrapper.eq(UserAuthorize::getAuthIdentifier, authIdentifier);
        UserAuthorize userAuthorize = userAuthorizeMapper.selectOne(wrapper);
        return userAuthorize != null ? Optional.of(userAuthorize) : Optional.error(ErrorCode.USER_NOT_EXIST);
    }

    public List<UserAuthorize> queryAuthorize(int userId) {
        final LambdaQueryWrapper<UserAuthorize> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserAuthorize::getUserId, userId);
        return userAuthorizeMapper.selectList(wrapper);
    }

    public Optional<UserAuthorize> queryAuthorize(int userId, int athType) {
        final LambdaQueryWrapper<UserAuthorize> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserAuthorize::getUserId, userId);
        wrapper.eq(UserAuthorize::getAuthType, athType);
        UserAuthorize userAuthorize = userAuthorizeMapper.selectOne(wrapper);
        return userAuthorize != null ? Optional.of(userAuthorize) : Optional.error(ErrorCode.USER_NOT_EXIST);
    }

    public Optional<UserProfile> queryProfile(int userId) {
        UserProfile userProfile = userProfileMapper.selectById(userId);
        return userProfile != null ? Optional.of(userProfile) : Optional.error(ErrorCode.USER_NOT_EXIST);
    }

    public Optional<UserProfile> queryProfile(String phone) {
        LambdaQueryWrapper<UserProfile> wrapper = MPWrapper.lambdaQuery(UserProfile.class);
        wrapper.eq(UserProfile::getPhone, phone);
        UserProfile userProfile = userProfileMapper.selectOne(wrapper);
        return userProfile != null ? Optional.of(userProfile) : Optional.error(ErrorCode.USER_NOT_EXIST);
    }

    public Set<Integer> queryRole(int userId) {
        LambdaQueryWrapper<UserRole> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserRole::getUserId, userId);
        return userRoleMapper.selectList(wrapper).stream().map(UserRole::getRoleId).collect(Collectors.toSet());
    }

    @Transactional
    public void updateAuthorizeById(UserAuthorize entity) {
        LambdaUpdateWrapper<UserAuthorize> wrapper = new LambdaUpdateWrapper<>();
        wrapper.set(StringUtils.hasText(entity.getAuthIdentifier()), UserAuthorize::getAuthIdentifier,
                entity.getAuthIdentifier());
        wrapper.set(StringUtils.hasText(entity.getAuthCredential()), UserAuthorize::getAuthCredential,
                entity.getAuthCredential());
        wrapper.set(StringUtils.hasText(entity.getAuthSalt()), UserAuthorize::getAuthSalt, entity.getAuthSalt());
        wrapper.set(StringUtils.hasText(entity.getAuthToken()), UserAuthorize::getAuthToken, entity.getAuthToken());
        wrapper.set(entity.getLoginTime() != null, UserAuthorize::getLoginTime, entity.getLoginTime());
        wrapper.eq(UserAuthorize::getUserId, entity.getUserId());
        wrapper.eq(UserAuthorize::getAuthType, entity.getAuthType());
        int row = userAuthorizeMapper.update(null, wrapper);
        Assert.isTrue(row == 1, ErrorCode.USER_AUTHORIZE_UPDATE);
    }

    public IPage<UserProfile> pageUser(UserPageReq req) {
        IPage<UserProfile> page = PageHelper.getPage(req);
        if (req.getRoleId() == null) {
            return userProfileMapper.selectPageUser(req, page);
        }
        return userProfileMapper.selectPageUserByRole(req, page);
    }

    public Optional<ConsoleUser> getUserDetail(int userId) {
        Optional<UserProfile> profileOptional = this.queryProfile(userId);
        if (profileOptional.fail()) {
            return Optional.error(profileOptional);
        }
        Optional<UserAuthorize> authorizeOptional = this.queryAuthorize(userId, 1);
        ConsoleUser consoleUser = new ConsoleUser(profileOptional.get(), authorizeOptional.orElse(null));
        Set<Integer> roleIds = this.queryRole(userId);
        for (Integer roleId : roleIds) {
            consoleUser.addRole(RoleCache.getById(roleId));
        }
        return Optional.of(consoleUser);
    }

    @Transactional
    public Optional addUser(ConsoleUser consoleUser) {
        Optional checkPhone = checkPhoneExist(consoleUser.getUserProfile().getPhone());
        if (checkPhone.fail()) {
            return checkPhone;
        }
        LOGGER.info("新增用户ID: {}", consoleUser.getUserId());
        userProfileMapper.insert(consoleUser.getUserProfile());
        userAuthorizeMapper.insert(consoleUser.getUserAuthorize());
        saveBatchUserRole(consoleUser.createdUserRoles(true));
        return Optional.success();
    }

    @Transactional
    public void saveBatchUserRole(Collection<UserRole> userRoles) {
        UserRole userRole = userRoles.stream().findFirst().orElse(null);
        Assert.notNull(userRole, ErrorCode.USER_ROLE_INSERT);
        LambdaQueryWrapper<UserRole> wrapper = MPWrapper.lambdaQuery(UserRole.class);
        wrapper.eq(UserRole::getUserId, userRole.getUserId());
        userRoleMapper.delete(wrapper);
        boolean save = userRoleMapper.saveBatch(userRoles) == userRoles.size();
        Assert.isTrue(save, ErrorCode.USER_ROLE_INSERT);
    }

    @Transactional
    public Optional editUser(ConsoleUser consoleUser) {
        Optional<UserProfile> optional = queryProfile(consoleUser.getUserId());
        if (optional.fail()) {
            return optional;
        }
        UserProfile orginalUserProfile = optional.get();
        UserProfile newUserProfile = consoleUser.getUserProfile();
        if (StringUtils.hasText(newUserProfile.getPhone()) && !orginalUserProfile.getPhone().equals(newUserProfile.getPhone())) {
            // 原手机号与新手机号不一致，检查新手机号注册状态
            Optional checkNewPhone = checkPhoneExist(newUserProfile.getPhone());
            if (checkNewPhone.fail()) {
                return checkNewPhone;
            }
        }
        userProfileMapper.updateById(newUserProfile);
        updateAuthorizeById(consoleUser.getUserAuthorize());
        saveBatchUserRole(consoleUser.createdUserRoles());
        return Optional.success();
    }

    /**
     * 检查手机号是否已存在
     *
     * @param phone 手机号
     * @return
     */
    public Optional checkPhoneExist(String phone) {
        Optional<UserProfile> optional = queryProfile(phone);
        if (optional.isPresent()) {
            return Optional.error(ErrorCode.USER_PHONE_EXIST);
        }
        return Optional.success();
    }
}
